import java.util.*;
import java.io.*;
import org.apache.commons.lang3.StringUtils;

class StudentCourseTable {

	private List<StudentCourse> lstStuCrse;
	Table stuCrseTable;

	int nStudents;
	ReadStatus enReadStatus;
	ErrorStatus enErr;

	public static final String strStuCourseTable  = new String(".\\StudentsCourseTable.csv");
	public static final String strStuCourseTableW = strStuCourseTable;//new String(".\\StudentsCourseTableVerify.csv");
	public static final String strDelim = new String(",");

	private boolean ReadStudentCourseTable() {
		if (enReadStatus == ReadStatus.READ_STU_COURSE_DATA) {
			enErr = ErrorStatus.ERR_DB_ALREADY_READ;
			return false;
		}

		stuCrseTable = new Table(strStuCourseTable, strDelim);
		if (stuCrseTable.IsErr()) {
			enErr = ErrorStatus.ERR_STU_COURSE_DATABASE;
			return false;
		}

		int nStudentsInDb = stuCrseTable.GetNumberOfLines();

		if (nStudentsInDb == 0) {
			enErr = ErrorStatus.ERR_COURSE_DATABASE_EMPTY;
			return false;
		}

		nStudents = 0;

		for (int n = 0; n < nStudentsInDb ; n++) {

			String strUId = new String(stuCrseTable.ReadAt(n, StudentCourseTableIndices.enIndxStuCourseStuId.enVal()));
			//System.out.println("Reading student: " + strUId);
			int nRegdCourses = Integer.parseInt(stuCrseTable.ReadAt(n, StudentCourseTableIndices.enIndxStuCourseNumCourses.enVal()));

			StudentCourse stuCrse = new StudentCourse(strUId);

			if (nRegdCourses > 0)
			{
				int nTableIndx = StudentCourseTableIndices.enIndxStuCourseNumCourses.enVal() + 1;

				for (int c = 0; c < nRegdCourses ; c++) {

					String strCrseIds = new String(stuCrseTable.ReadAt(n, nTableIndx));
					String strCrseGrds = new String(stuCrseTable.ReadAt(n, nTableIndx+1));

					stuCrse.AddCourseAndGrade(strCrseIds, strCrseGrds);
					nTableIndx += 2;
				}
			}

			lstStuCrse.add(stuCrse);
			nStudents++;
		}

		enReadStatus = ReadStatus.READ_STU_COURSE_DATA;
		return true;
	}

	// First time construct or corrupted file this should never happen
	// need to add redundancy later for regd courses  we can for now set them to 0
	// if file is corrupted! need a try catch here later...
	private boolean CreateStudentCourseTable(UserDatabase usrDb) {
		nStudents = usrDb.NumberOfStudents();
		String [] stuIds = usrDb.GetAllStudentsbyId();
		for (int n = 0; n < nStudents ; n++) {
			StudentCourse stucrse = new StudentCourse(stuIds[n]);
			lstStuCrse.add(stucrse);
		}

		return (nStudents > 0);
	}

	StudentCourseTable(UserDatabase usrDb) {
		lstStuCrse  = new ArrayList<StudentCourse>();
		nStudents = 0;
		enReadStatus = ReadStatus.READ_INIT;
		enErr = ErrorStatus.ERR_NOT_READY;

		if (!this.ReadStudentCourseTable()) {
			enErr = ErrorStatus.ERR_NOT_READY;

			if (this.CreateStudentCourseTable(usrDb) == false)
				enErr = ErrorStatus.ERR_STU_COURSE_DATABASE_EMPTY;
				else
					enErr = ErrorStatus.NO_ERRORS;
		}
		else
			enErr = ErrorStatus.NO_ERRORS;

		enReadStatus =  ReadStatus.READ_STU_COURSE_DATA;
	}

	public List<String> getRegisteredCourseList(String strStuId) {
		List<String> studentList = new ArrayList<String>();
		if (nStudents == 0)
			return null;

		for (int n = 0; n < nStudents ; n++) {
			StudentCourse stuCrse = lstStuCrse.get(n);
			String stuId = stuCrse.getStudentId();
			//System.out.println("Comparing students: " + stuId + " With: " + strStuId);
			if (StringUtils.equals(stuId, strStuId)) {
				studentList = stuCrse.getRegCourses();
			}
		}
		return studentList;
	}

	public StudentCourse GetStudentCourseById(String strStuId) {

		//System.out.println("# of students: " + nStudents);
		if (nStudents == 0)
			return null;

		for (int n = 0; n < nStudents ; n++) {
			StudentCourse stuCrse = lstStuCrse.get(n);
			String stuId = stuCrse.getStudentId();
			//System.out.println("Comparing students: " + stuId + " With: " + strStuId);
			if (StringUtils.equals(stuId, strStuId)) {
				return stuCrse;
			}
		}

		System.out.println("Student not found: "+ strStuId);
		return null;
	}

	private int GetStudentTableIndex(String strStuId) {
		if (nStudents == 0)
			return -1;

		for (int n = 0; n < nStudents ; n++) {
			StudentCourse stuCrse = lstStuCrse.get(n);
			String stuId = stuCrse.getStudentId();
			if (StringUtils.equals(stuId, strStuId)) {
				return n;
			}
		}

		return -1;
	}

	public boolean isExistStudent(String strStuId) {
		return (this.GetStudentCourseById(strStuId) != null);
	}

	public boolean RegisterStudentToCourse(UserDatabase usrDb, String strStuId, String strCrseId) {
		StudentCourse stuCrse = this.GetStudentCourseById(strStuId);
		if (stuCrse == null) {
			//System.out.printf("FAIL to get StudentCourseById ID:(%s)", strStuId);
			return false;
		}
		int nStuTIndx = this.GetStudentTableIndex(strStuId);
		if (nStuTIndx == -1) {
			//System.out.println("FAIL2");
			return false;
		}
		int nColIndx = StudentCourseTableIndices.enIndxStuCourseNumCourses.enVal();
		//System.out.println("Index: " + nStuTIndx);

		if (stuCrse.RegisterCourseToStudent(strCrseId)) {
			usrDb.SetCoursesRegdForStudent(strStuId, stuCrse.NumberOfCoursesRegistered());
			stuCrseTable.AddColumnToRow(nStuTIndx, strCrseId);
			stuCrseTable.AddColumnToRow(nStuTIndx, new String("-"));
			stuCrseTable.SetStringAt(nStuTIndx, nColIndx, Integer.toString(stuCrse.NumberOfCoursesRegistered()));
			usrDb.UpdateDatabase();
			UpdateDatabase();
		 }
		 else {
			//System.out.println("FAIL3");
		 	return false;
		}

		return true;
	}

	public boolean DropCourseForStudent(UserDatabase usrDb, String strStuId, String strCrseId) {
		StudentCourse stuCrse = this.GetStudentCourseById(strStuId);
		if (stuCrse == null)
			return false;

		int nStuTIndx = this.GetStudentTableIndex(strStuId);
		if (nStuTIndx == -1)
			return false;

		int nColIndx = StudentCourseTableIndices.enIndxStuCourseNumCourses.enVal();

		if (stuCrse.DropACourseForStudent(strCrseId)) {
			usrDb.SetCoursesRegdForStudent(strStuId, stuCrse.NumberOfCoursesRegistered());
			int nCrseIndx = stuCrseTable.GetColumnIndexForString(nStuTIndx, strCrseId);
			if (nCrseIndx == -1)
				return false;
			stuCrseTable.RemoveColumnFromRow(nStuTIndx, nCrseIndx, 2);
			stuCrseTable.SetStringAt(nStuTIndx, nColIndx, Integer.toString(stuCrse.NumberOfCoursesRegistered()));
			usrDb.UpdateDatabase();
			return UpdateDatabase();
		}
		 else
		 	System.out.println("Student not regd for course..");

		return false;
	}

	public boolean AddStudent(String strId) {
		StudentCourse stuCrse = new StudentCourse(strId);

		lstStuCrse.add(stuCrse);
		stuCrseTable.AddALine(stuCrse.toString());

		return stuCrseTable.WriteToFile(strStuCourseTableW);
	}

	public int GetNumberOfCoursesRegistered(String strStuId) {
		StudentCourse stuCrse = this.GetStudentCourseById(strStuId);
		if (stuCrse == null)
			return 0;

		return stuCrse.NumberOfCoursesRegistered();
	}

	public String GetStudentGrades(String strStuId, String crseId) {
		StudentCourse stuCrse = this.GetStudentCourseById(strStuId);
		if (stuCrse == null)
			return null;

		return stuCrse.GetGradeForCourse(crseId);
	}

	public boolean SetStudentGrade(String strStuId, String strCrseId, String strGrade) {
		StudentCourse stuCrse = this.GetStudentCourseById(strStuId);
		if (stuCrse == null)
			return false;

		return stuCrse.AssignGradeForCourse(strCrseId, strGrade);
	}

	public String GetStudentsCourseLog(String strStuId) {
		StudentCourse stuCrse = this.GetStudentCourseById(strStuId);
		if (stuCrse == null)
			return null;

		return stuCrse.toString();
	}

	public StudentTableIndices[] AllStudentTableIndices()
	{
		StudentTableIndices[] enIndcs = new StudentTableIndices[18];
		enIndcs[0] = StudentTableIndices.enIndxStudentUid;
		enIndcs[1] = StudentTableIndices.enIndxStudentId;
		enIndcs[2] = StudentTableIndices.enIndxStudentFName;
		enIndcs[3] = StudentTableIndices.enIndxStudentMName;
		enIndcs[4] = StudentTableIndices.enIndxStudentLName;
		enIndcs[5] = StudentTableIndices.enIndxStudentAddrs1;
		enIndcs[6] = StudentTableIndices.enIndxStudentAddrs2;
		enIndcs[7] = StudentTableIndices.enIndxStudentAddrsCity;
		enIndcs[8] = StudentTableIndices.enIndxStudentAddrsState;
		enIndcs[9] = StudentTableIndices.enIndxStudentAddrsZip;
		enIndcs[10] = StudentTableIndices.enIndxStudentEmail;
		enIndcs[11] = StudentTableIndices.enIndxStudentAge;
		enIndcs[12] = StudentTableIndices.enIndxStudentGender;
		enIndcs[13] = StudentTableIndices.enIndxStudentSSN;
		enIndcs[14] = StudentTableIndices.enIndxStudentYearOfStudy;
		enIndcs[15] = StudentTableIndices.enIndxStudentEnrollmentStatus;
		enIndcs[16] = StudentTableIndices.enIndxStudentEnrolledMajor;
		enIndcs[17] = StudentTableIndices.enIndxStudentNRegdCourses;

		return enIndcs;
	}

	public boolean UpdateDatabase() {
			return stuCrseTable.WriteToFile(strStuCourseTableW);
	}

	public boolean CloseDatabase() {
		//stuCrseTable.PrintTable();
		if (stuCrseTable.WriteToFile(strStuCourseTableW) == false)
			return false;

		return true;
	}
}
